﻿using System;

using PostSharp.Aspects;
using BizElements.BusinessLayer;

namespace BizElements.BusinessLayer.Aspects
{
    /// <summary>Enforces optimistic concurrency control. Generates <see cref="BrokenRuleException"/> if the provided object instance is NOT based on current/latest version of data, i.e. if other user/process has changed the object in the database.
    /// Applicable only to objects that implement <see cref="BizElements.BusinessLayer.IBusinessObject"/> interface.</summary>
    /// <remarks><para>During the build process the code which enforces optimistic concurrency control is automatically injected and intercepts 
    /// the method marked with <b>OptimisticConcurrencyControlAttribute</b> attribute.</para>
    /// <para>To use this attribute you have to install PostSharp 2 on your development computer and add references to <b>PostSharp.dll</b> assembly to your project.</para></remarks>
    /// <example>The following example demonstrates how to use the attribute:
    /// <code>
    /// [OptimisticConcurrencyControl]
    /// public void Save()
    /// {
    ///     new EntityDao(this).Save();
    /// }
    /// </code>
    /// The previous example is functionally identical to the following pattern:
    /// <code>
    /// public void Save()
    /// {
    ///     LockUtil.EnforceConcurrencyControl(this);
    ///     new EntityDao(this).Save();
    /// }
    /// </code>
    /// </example>
    [Serializable]
    [AttributeUsage(AttributeTargets.Method)]
    public sealed class OptimisticConcurrencyControlAttribute : OnMethodBoundaryAspect
    {
        /// <summary>50 - higher priority than TransactionRequired so that transaction is started after concurrency control.</summary>
        internal const int Priority = 50;

        /// <summary>
        /// Initializes a new instance of <b>OptimisticConcurrencyControlAttribute</b> class and sets its <b>AspectPriority</b>.
        /// </summary>
        public OptimisticConcurrencyControlAttribute()
        {
            AspectPriority = Priority;
        }

        /// <summary>Generates <see cref="BrokenRuleException"/> if the provided object instance is NOT based on current/latest version of data, i.e. if other user/process has changed the object in the database. 
        /// Executed <b>before</b> the body of methods to which this aspect is applied. </summary>
        /// <param name="eventArgs">Event arguments specifying which method is being executed, which are its arguments, and how should the execution continue after the execution of <b>OnEntry</b>.</param>
        public sealed override void OnEntry(MethodExecutionArgs eventArgs)
        {
            IBusinessObject bizObject = eventArgs.Instance as IBusinessObject;
            if (bizObject != null)
                LockUtil.EnforceConcurrencyControl(bizObject);
        }
    }
}
